#include "base.h"
#include "string.h"
#include "stdlib.h"
#include "stdarg.h"
#include "debug.h"
#include "ktty.h"

#define UNSIGNED	0
#define SIGNED		1

#define PAD_NULL	0
#define PAD_SPACE	1
#define PAD_ZERO	2


typedef char *(*char_handler)(uint_32 param_p, int padding, int print_len);


static char *number2str(unsigned int num,int base, int signed_num, int padding, int print_len){
	static char num2ch[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f',
						'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
						'w','x','y','z'};

	static char num2ch_buf[64] = { [0 ... 63] = 0};
	char temp_buf[64];

	int num_bit;
	int num_len;
	
	BOOL negative = FALSE;

	int i = 0;

	char padding_ch;

	if ((signed_num == SIGNED) && (num < 0)){
		negative = TRUE;
		num = -num;
	}

	/* get each bit of the number */
	for (i = 0; num ; i++, num /= base){
		num_bit = num % base;
		temp_buf[i] = num2ch[num_bit];
	}
	
	if (padding == PAD_ZERO){
		padding_ch = '0';
	}else{
		padding_ch = ' ';
	}
	
	if (print_len > i){
		for ( ; i < print_len; i++){
			temp_buf[i] = padding_ch;
		}
	}

	if (negative == TRUE){
		temp_buf[i] = '-';
		i ++;
	}

	num_len = i;

	/* revert the buffer */
	for (i = 0; i < num_len ; i++){
		num2ch_buf[i] = temp_buf[num_len - 1 - i];
	}

	num2ch_buf[num_len] = 0;
	return num2ch_buf;
}

static char * do_esc_unsigned(uint_32 num_p, int padding, int print_len){
	char *esc_unsg_buf = NULL;
	
	esc_unsg_buf = number2str(*(unsigned int *)num_p, 8, UNSIGNED, padding, print_len);
	
	return esc_unsg_buf;
}

static char * do_esc_oct(uint_32 num_p, int padding, int print_len){
	char *esc_oct_buf = NULL;
	
	esc_oct_buf = number2str(*(unsigned int *)num_p, 8, SIGNED, padding, print_len);
	
	return esc_oct_buf;
}

static char * do_esc_dec(uint_32 num_p, int padding, int print_len){
	char *esc_dec_buf = NULL;
	
	esc_dec_buf = number2str(*(unsigned int *)num_p, 10, SIGNED, padding, print_len);
	
	return esc_dec_buf;
}

static char *do_esc_hex(uint_32 num_p, int padding, int print_len){
	char *esc_hex_buf = NULL;
	
	esc_hex_buf = number2str(*(unsigned int *)num_p, 16, UNSIGNED, padding, print_len);
	
	return esc_hex_buf;
}

static char *do_esc_char(uint_32 ch_p, int padding, int print_len){
	static char esc_char_buf[16] = { [0 ... 15] = 0};


	if ((*(char *)ch_p >= 32) && (*(char *)ch_p <= 126)){
		esc_char_buf[0] = *(char *)ch_p;
	}

	esc_char_buf[1] = 0;
	return esc_char_buf;
}

static char *do_esc_string(uint_32 str_p, int padding, int print_len){
	static char esc_str_buf[256] = { [0 ... 255] = 0};
	char **__str_p = (char **)str_p;

	strcpy(esc_str_buf,*__str_p);

	return esc_str_buf;
}

static char * do_escape_char(uint_32 param_p, char cur_type, int padding, int print_len){
	char *esc_buf = NULL;

	char_handler do_char_handler = NULL;

	switch(cur_type){
	case 'd':
		do_char_handler = do_esc_dec;
		break;
	case 'x':
		do_char_handler = do_esc_hex;
		break;
	case 'c':
		do_char_handler = do_esc_char;
		break;
	case 's':
		do_char_handler = do_esc_string;
		break;
	default:
		break;
	}

	esc_buf = do_char_handler(param_p,padding,print_len);

	return esc_buf;
}


static char * do_normal_char(uint_32 ch_p){
	static char normal_buf[16];
	normal_buf[0] = *(char *)ch_p;
	normal_buf[1] = 0;

	return normal_buf;
}

#define round_to_next(arg_p, cur_type)\
{\
	switch((cur_type)){\
	case 'd':\
		va_arg(arg_p,int);\
		break;\
	case 'x':\
		va_arg(arg_p,int);\
		break;\
	case 'c':\
		va_arg(arg_p,int);\
		break;\
	case 's':\
		va_arg(arg_p,int);\
		break;\
	default:\
		break;\
	}\
}

/* the max padding length is 32  */
/* the max number lenght between '%' and 'd'(or 'x' or 's' or 'c') is 16 */
#define is_zero(ch)		((ch) == '0')
#define is_digit(ch)	(((ch) >= '0') && ((ch) <= '9'))
#define is_esc_ch(ch)	(((ch) == 'd') || ((ch) == 'x') || ((ch) == 's') || ((ch) == 'c'))

static BOOL is_escape_pattern(char **fmt, int *padding, int *print_len){	
	BOOL is_esc_patn = FALSE;
	char *current = *fmt;
	char *next = *fmt + 1;
	
	*padding = PAD_SPACE;
	*print_len = 0;
	
	if (*current == '%') {
		for ( ; (next - current) < 16 ; next++){
			if ((next == current + 1) && is_zero(*next)){
				*padding = PAD_ZERO;
			}else if (is_esc_ch(*next)){
				break;
			}else if (is_digit(*next)){
				/* continue loop */
			}else{
				/* do nothing, continue loop */
			}
		}
		
		if ((next - current) > 16){
			is_esc_patn = FALSE;
		}else{
			*print_len = atoin(current + 1, next - current - 1);
			if (*print_len > 32){
				*print_len = 32;
			}
			*fmt = next;
			is_esc_patn = TRUE;
		}
	}
	
	return is_esc_patn;
}


int kprintf(char *fmt,...){
	char buf[256];				/* print one line*/
	int i = 0;						/* buffer index */

	char *cpy_p = NULL;

	va_list p_start = NULL;
	
	int padding;
	int print_len;

	if (fmt == NULL){
		return 0;
	}

	va_start(p_start,fmt);

	while(*fmt){
		if (is_escape_pattern(&fmt, &padding, &print_len)){
			cpy_p = do_escape_char((uint_32)p_start, *fmt, padding, print_len);
			round_to_next(p_start, *fmt);
		}else{
			cpy_p = do_normal_char((uint_32)fmt);
		}

		while(*cpy_p){
			buf[i] = *cpy_p;
			cpy_p ++;
			i ++ ;
		}

		fmt++;
	}
	
	buf[i] = 0;
	kputs(buf);
	
	va_end(p_start);
	
	return 0;
}
/* EOF */


